"use strict";

/**
 * 数组去重输出，不修改源数组
 * ```
 * @param SourceArray : Array 需要去重的数据源
 * @param {{
 * Refuse_Null: boolean,
 * Refuse_Undefined: boolean,
 * Refuse_EmptyArray:  boolean,
 * Data_StringFullEqual_NoAdd_NotCaseSensitive: boolean,
 * Data_NumberCountContentEqual_NoAdd_NotMatchCountSymbolRecordWay: boolean,
 * Function_EqualName_NoAdd_NotCaseSensitive: boolean,
 * AsyncFunction_EqualName_NoAdd_NotCaseSensitive: boolean
 * }} [option] : Object 去重操作参数
 *
 * @return Array
 *
 * @description option 参数对象属性
 *```
 * @description Refuse_Null: true，拒绝Null，布尔值
 * ```
 * @description Refuse_Undefined: true，拒绝Undefined，布尔值
 * ```
 * @description Refuse_EmptyObject: true，拒绝空对象，布尔值
 * ```
 * @description Refuse_EmptyArray: true，拒绝空数组，布尔值
 * ```
 * @description Data_StringFullEqual_NoAdd_NotCaseSensitive: true，字符串全匹配的不添加（不区分大小写），布尔值
 * ```
 * @description Data_NumberCountContentEqual_NoAdd_NotMatchCountSymbolRecordWay: true，记数符号相同的不添加（不区分记录方式），布尔值
 * ```
 * @description Function_EqualName_NoAdd_NotCaseSensitive: true，函数名称相同的不添加（不区分大小写），布尔值
 * ```
 * @description AsyncFunction_EqualName_NoAdd_NotCaseSensitive: true，异步函数名称相同的不添加（不区分大小写），布尔值
 * ```
 * */
function arrayRemoveDump(SourceArray, option) {
    let FinalArrayOutput;
    let currentNeedComp;
    if (Object.prototype.toString.call(SourceArray) === '[object Array]' && SourceArray.length > 0) {
        let useOption = false;
        let OptionData = {};
        if (Object.prototype.toString.call(option) === '[object Object]') {
            for (let chkItem in option) {
                switch (chkItem.toLowerCase()) {
                    case 'refuse_null':
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Refuse_Null = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case 'refuse_undefined':
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Refuse_Undefined = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case 'refuse_emptyarray':
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Refuse_EmptyArray = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case 'refuse_emptyobject':
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Refuse_EmptyObject = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case "data_stringfullequal_noadd_notcasesensitive":
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Data_StringFullEqual_NoAdd_NotCaseSensitive = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case "data_numbercountcontentequal_noadd_notmatchcountsymbolrecordway":
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Data_NumberCountContentEqual_NotMatchCountSymbolRecordWay = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case "function_equalname_noadd_notcasesensitive":
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.Function_EqualName_NoAdd_NotCaseSensitive = option[chkItem];
                        }
                        useOption = true;
                        break;
                    case "asyncfunction_equalname_noadd_notcasesensitive":
                        if (Object.prototype.toString.call(option[chkItem]) === '[object Boolean]') {
                            OptionData.AsyncFunction_EqualName_NoAdd_NotCaseSensitive = option[chkItem];
                        }
                        useOption = true;
                        break;
                    default:
                        console.info("出现未知的配置选项，该选项不生效。");
                        break;
                }
            }
        }
        FinalArrayOutput = [];
        for (let num1 = 0; num1 < SourceArray.length; num1++) {
            currentNeedComp = SourceArray[num1];
            if (FinalArrayOutput.length === 0) {
                FinalArrayOutput.push(currentNeedComp);
            }
            for (let num2 = 0; num2 < SourceArray.length; num2++) {
                if (num1 === num2) continue;
                if (currentNeedComp !== SourceArray[num2]) {
                    let cct = SourceArray[num2];
                    let dff = true;
                    for (let tmp in FinalArrayOutput) {
                        switch (Object.prototype.toString.call(FinalArrayOutput[tmp])) {
                            case "[object Number]":
                                if (useOption) {
                                    if (OptionData.Data_NumberCountContentEqual_NotMatchCountSymbolRecordWay) {
                                        if (Object.prototype.toString.call(cct) === '[object String]') {
                                            if (FinalArrayOutput[tmp] === parseInt(cct)) dff = false;
                                        }
                                        if (FinalArrayOutput[tmp] === cct) dff = false;
                                    } else {
                                        if (FinalArrayOutput[tmp] === cct) dff = false;
                                    }
                                } else {
                                    if (cct === FinalArrayOutput[tmp]) dff = false;
                                }
                                break;
                            case "[object String]":
                                if (useOption) {
                                    if (OptionData.Data_StringFullEqual_NoAdd_NotCaseSensitive) {
                                        if (Object.prototype.toString.call(cct) === '[object String]') {
                                            if (cct.toLowerCase() === FinalArrayOutput[tmp].toLowerCase()) dff = false;
                                        }
                                        if (Object.prototype.toString.call(cct) === '[object Number]') {
                                            if (String(cct).toLowerCase() === FinalArrayOutput[tmp].toLowerCase()) dff = false;
                                        }
                                    } else {
                                        if (cct === FinalArrayOutput[tmp]) dff = false;
                                    }
                                } else {
                                    if (cct === FinalArrayOutput[tmp]) dff = false;
                                }
                                break;
                            case "[object Object]":
                                if (Object.prototype.toString.call(cct) === '[object Object]') {
                                    let cp1 = false;
                                    let cp2 = false;
                                    for (let cpx1 in cct) {
                                        cp1 = true;
                                        break;
                                    }
                                    for (let cpx2 in FinalArrayOutput[tmp]) {
                                        cp2 = true;
                                        break;
                                    }
                                    if (cp1 === cp2) {
                                        dff = false;
                                    }
                                }
                                break;
                            case "[object Array]":
                                if (Object.prototype.toString.call(cct) === '[object Array]') {
                                    if (cct.length === FinalArrayOutput[tmp].length) dff = false;
                                }
                                break;
                            case "[object Function]":
                                if (Object.prototype.toString.call(cct) === "[object Function]") {
                                    if (useOption) {
                                        if (OptionData.Function_EqualName_NoAdd_NotCaseSensitive) {
                                            if (cct.name.toLowerCase() === FinalArrayOutput[tmp].name.toLowerCase()) dff = false;
                                        } else {
                                            if (cct.name === FinalArrayOutput[tmp].name) dff = false;
                                        }
                                    } else {
                                        if (cct.name === FinalArrayOutput[tmp].name) dff = false;
                                    }
                                }
                                break;
                            case "[object AsyncFunction]":
                                if (Object.prototype.toString.call(cct) === "[object AsyncFunction]") {
                                    if (useOption) {
                                        if (OptionData.AsyncFunction_EqualName_NoAdd_NotCaseSensitive) {
                                            if (cct.name.toLowerCase() === FinalArrayOutput[tmp].name.toLowerCase()) dff = false;
                                        } else {
                                            if (cct.name === FinalArrayOutput[tmp].name) dff = false;
                                        }
                                    } else {
                                        if (cct.name === FinalArrayOutput[tmp].name) dff = false;
                                    }
                                }
                                break;
                            default:
                                if (cct === FinalArrayOutput[tmp]) {
                                    dff = false;
                                }
                                break;
                        }
                    }
                    if (dff) {
                        if (useOption) {
                            let confirmAdd = true;
                            switch (Object.prototype.toString.call(cct)) {
                                case "[object Undefined]":
                                    if (OptionData.Refuse_Undefined) confirmAdd = false;
                                    break;
                                case "[object Null]":
                                    if (OptionData.Refuse_Null) confirmAdd = false;
                                    break;
                                case "[object Object]":
                                    if (OptionData.Refuse_EmptyObject) {
                                        let ccp1 = true;
                                        for (let cpp1 in cct) {
                                            ccp1 = false;
                                            break;
                                        }
                                        if (ccp1) confirmAdd = false;
                                    }
                                    break;
                                case "[object Array]":
                                    if (OptionData.Refuse_EmptyArray) {
                                        if (cct.length === 0) confirmAdd = false;
                                    }
                                    break;
                            }
                            if (confirmAdd) FinalArrayOutput.push(cct);
                        } else {
                            FinalArrayOutput.push(cct);
                        }
                    }
                }
            }
        }
    }
    return FinalArrayOutput;
}

/**
 * 任一数据转换成Base64
 * ```
 *@name anyDataConvertBase64
 *@param dataSource : any 需要转换的数据源
 *@param {{
 *  JoinTypeLabel: boolean,
 *  content: array
 * }} convertOptions 转换选项对象
 *
 * @return string
 *
 * convertOptions 转换选项对象属性
 * ```
 * JoinTypeLabel 加入浏览器等应用可识别使用的Base64数据类型标识，默认值为false，输出纯Base64数据，不带任何标识
 * ```
 * content 自定义引入的类型标识数据对象的数组，extName为文件拓展名(后缀)，type为类型标识
 * ```
 * convertOptions的配置选项仅在文件转换操作有效。
 * ```
 * 本函数内置了常用的类型标识数据，平常的文件转换要求均可满足。
 * ```
 *
 * @example
 * //自定义使用类型标识
 * let convertResult = anyDataConvertBase64("./test.jpg",{
 *      JoinTypeLabel:true,
 *      content:[
 *          {extName: "txt,log", type: "text/plain"},
 *          {extName: "htm,html", type: "text/html"},
 *          {extName: "wma", type: "audio/x-ms-wma"},
 *          {extName: "wmv", type: "video/x-ms-wmv"},
 *          {extName: "bmp", type: "image/bmp"},
 *          {extName: "png", type: "image/png"}
 *          {extName: "crx", type: "application/x-chrome-extension"},
 *          {extName: "php", type: "application/x-php"}
 *      ]
 * })
 *
 * //不使用自定义类型、使用内置定义类型
 * let convertResult = anyDataConvertBase64("./test.jpg",{
 *      JoinTypeLabel:true
 * })
 *
 * //转换输出纯Base64的数据
 * let convertResult = anyDataConvertBase64("./test.jpg",{
 *      JoinTypeLabel:false,
 * })
 *
 * //转换纯Base64数据时，convertOptions对象参数可无需传入。
 * */
function anyDataConvertBase64(dataSource, convertOptions) {
    let counts = 0;
    let sourceBase64 = undefined;
    let OptionObject = {
        JoinTypeLabel: false,
        contentTypeList:
            [
                {extName: "txt,log", type: "text/plain"},
                {extName: "reg", type: "text/x-ms-regedit"},
                {extName: "md", type: "text/markdown"},
                {extName: "htm,html", type: "text/html"},
                {extName: "xml", type: "text/xml"},
                {extName: "js", type: "text/javascript"},
                {extName: "css", type: "text/css"},
                {extName: "scss", type: "text/x-scss"},
                {extName: "sass", type: "text/x-sass"},
                {extName: "wma", type: "audio/x-ms-wma"},
                {extName: "mid", type: "audio/midi"},
                {extName: "mp3", type: "audio/mpeg"},
                {extName: "wmv", type: "video/x-ms-wmv"},
                {extName: "avi", type: "video/x-msvideo"},
                {extName: "mp4", type: "video/mp4"},
                {extName: "jpg,jpeg", type: "image/jpeg"},
                {extName: "png", type: "image/png"},
                {extName: "gif", type: "image/gif"},
                {extName: "webp", type: "image/webp"},
                {extName: "psd", type: "image/vnd.adobe.photoshop"},
                {extName: "cur", type: "image/x-win-bitmap"},
                {extName: "bmp", type: "image/bmp"},
                {extName: "ttf", type: "font/ttf"},
                {extName: "woff", type: "font/woff"},
                {extName: "woff2", type: "font/woff2"},
                {extName: "ttc", type: "font/collection"},
                {extName: "pdf", type: "application/pdf"},
                {extName: "doc,docx", type: "application/msword"},
                {extName: "zip", type: "application/zip"},
                {extName: "7z", type: "application/x-7z-compressed"},
                {extName: "exe", type: "application/x-ms-dos-executable"},
                {extName: "jar", type: "application/x-java-archive"},
                {extName: "crx", type: "application/x-chrome-extension"},
                {extName: "php", type: "application/x-php"},
                {extName: "sql", type: "application/sql"},
                {extName: "svg", type: "image/svg+xml"},
                {extName: "img", type: "application/x-raw-disk-image"},
            ]
    }
    let fs = require("fs");

    function notEmptyDetect(data) {
        let detectValue = undefined;
        if (Object.prototype.toString.call(data) === "[object Array]") {
            if (data.length > 0) {
                detectValue = true;
            } else {
                detectValue = false;
            }
        } else if (Object.prototype.toString.call(data) === "[object Object]") {
            let count = 0;
            for (let num in data) {
                count++;
                break;
            }
            if (count > 0) {
                detectValue = true;
            } else {
                detectValue = false;
            }
        }
        return detectValue;
    }

    if (Object.prototype.toString.call(convertOptions) === "[object Object]" && notEmptyDetect(convertOptions) === true) {
        function content_Type_detect(source) {
            let detect_value;

            function obj_NotEmpty_valid(obj, need_detect_item, strictMode) {
                let valid_value;
                try {
                    if (Object.prototype.toString.call(need_detect_item) === "[object Array]" && need_detect_item.length > 0) {
                        let validCount = 0;
                        for (let num in need_detect_item) {
                            if (strictMode) {
                                if (typeof obj[need_detect_item[num]] !== "undefined") {
                                    validCount++;
                                }
                            } else {
                                for (let num1 in obj) {
                                    if (String(num1).toLowerCase() === String(need_detect_item[num]).toLowerCase()) {
                                        validCount++;
                                    }
                                }
                            }
                        }
                        if (validCount !== need_detect_item.length) {
                            throw new Error("");
                        }
                    } else {
                        throw new Error("");
                    }
                    valid_value = true;
                } catch (e) {
                    valid_value = false;
                }
                return valid_value;
            }

            try {
                if (Object.prototype.toString.call(source) === "[object Array]" && source.length > 0) {
                    for (let num in source) {
                        if (!obj_NotEmpty_valid(source[num], ['extName', 'type'])) {
                            throw new Error("")
                        }
                    }
                } else {
                    throw new Error("");
                }
                detect_value = true;
            } catch (e) {
                detect_value = false;
            }
            return detect_value;
        }

        for (let item in convertOptions) {
            switch (String(item).toLowerCase()) {
                case "jointypelabel":
                    if (typeof convertOptions[item] === "boolean") {
                        OptionObject.JoinTypeLabel = convertOptions[item];
                        counts++
                    }
                    break;
                case "content":
                    if (content_Type_detect(convertOptions[item])) {
                        OptionObject.contentTypeList = convertOptions[item];
                        counts++
                    }
                    break;
            }
        }
    }
    if (fs.existsSync(dataSource) && fs.statSync(dataSource).isFile()) {
        sourceBase64 = fs.readFileSync(dataSource, 'base64');
        if (OptionObject.JoinTypeLabel) {
            let extName = String(dataSource).split(".");
            let header_base64;
            extName = extName[extName.length - 1];
            for (let num in OptionObject.contentTypeList) {
                if (String(OptionObject.contentTypeList[num].extName).toLowerCase().includes(String(extName).toLowerCase())) {
                    header_base64 = `data:${OptionObject.contentTypeList[num].type};base64,`;
                    break;
                }
            }
            if (typeof header_base64 === "string") {
                sourceBase64 = header_base64 + sourceBase64;
            } else {
                header_base64 = `data:application/octet-stream;base64,`;
                sourceBase64 = header_base64 + sourceBase64;
            }
        }
    } else {
        let EnBase64code = Buffer.from(dataSource);
        sourceBase64 = EnBase64code.toString('base64');
    }
    return sourceBase64;
}

/**
 * 擦除数组中匹配的数据
 * ```
 * @name EraseMatchArrayItems
 * @param sourceArray : Array 需要操作的数组数据源
 * @param needEraseItem : any 指定需要擦除的数据
 * @param notModifySourceArray : boolean 不更改源数组
 * @return Array|undefined
 * */
function EraseMatchArrayItems(sourceArray, needEraseItem, notModifySourceArray) {
    if (typeof notModifySourceArray !== "boolean") {
        notModifySourceArray = false;
    }
    if (Object.prototype.toString.call(sourceArray) !== "[object Array]") {
        throw new Error("sourceArray Argument must be an array");
    }

    function deepCopy(source) {
        var cp1;
        var cp2;
        try {
            cp1 = JSON.stringify(source);
            cp2 = JSON.parse(cp1);
            return cp2;
        } catch (e) {
            return source;
        }
    }

    if (needEraseItem !== undefined) {
        var final = [];
        var stopLoop = false;
        while (true) {
            var funcResult = undefined;
            try {
                sourceArray.forEach(function (value, index, array) {
                    switch (typeof needEraseItem) {
                        case "function":
                            if (Object.prototype.toString.call(needEraseItem) === "[object Function]") {
                                funcResult = needEraseItem(value);
                                if (funcResult !== undefined) {
                                    if (typeof funcResult === "boolean") {
                                        if (funcResult) {
                                            if (!notModifySourceArray) {
                                                throw index;
                                            }
                                        } else {
                                            if (notModifySourceArray) {
                                                final.push(deepCopy(value));
                                            }
                                        }
                                    }
                                }
                            } else {
                                throw new Error("is Only Support Not Async Functions!");
                            }
                            break;
                        case "string":
                            if (String(needEraseItem).toString() === String(value).toString()) {
                                if (!notModifySourceArray) {
                                    throw index;
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        case "number":
                            if (Number(needEraseItem).valueOf() === Number(value).valueOf()) {
                                if (!notModifySourceArray) {
                                    throw index;
                                }
                            } else {
                                if (notModifySourceArray) {
                                    final.push(deepCopy(value));
                                }
                            }
                            break;
                        default:
                            throw new Error("needEraseItem is Only Support String Or Number Or Functions");
                    }
                });
                stopLoop = true;
            } catch (e) {
                stopLoop = false;
                sourceArray.splice(e, 1);
            }
            if (stopLoop) {
                break;
            }
        }
        if (notModifySourceArray) {
            return final;
        } else {
            return undefined;
        }
    } else {
        throw new Error("not Exist need Match Item");
    }
}

module.exports = {arrayRemoveDump, anyDataConvertBase64, EraseMatchArrayItems};